import java.util.*;
import java.io.*;
import org.apache.commons.lang3.StringUtils;


class UserDatabase
{
	private List<Student> lstStudents;
	private List<Teacher> lstTeachers;
	int nStudents;
	int nTeachers;
	ReadStatus enReadStatus;
	ErrorStatus enErr;
	Table stuTable;
	Table tchrTable;

	public static final String strDatabaseFileStudents  = new String(".\\StudentsDataFile.csv");
	public static final String strDatabaseFileTeachers = new String(".\\TeacherDataFile.csv");
	public static final String strDatabaseFileStudentsW  = strDatabaseFileStudents;//new String(".\\StudentsDataFileVerify.csv");
	public static final String strDatabaseFileTeachersW = strDatabaseFileTeachers;//new String(".\\TeacherDataFileVerify.csv");
	public static final String strDelim = new String(",");

	private void PrintTableAt(Table aTable, int atRow, int atCol) {
		System.out.println("R,C = "+atRow+", "+atCol+" : "+aTable.ReadAt(atRow, atCol));
	}

	private boolean ReadStudents() {

		if (enReadStatus == ReadStatus.READ_ALL) {
			enErr = ErrorStatus.ERR_DB_ALREADY_READ;
			return false;
		}



		stuTable = new Table(strDatabaseFileStudents, strDelim);
		if (stuTable.IsErr()) {
			enErr = ErrorStatus.ERR_STUDENT_DATABASE;
			return false;
		}

		int nStudentsInDb = stuTable.GetNumberOfLines();

		if (nStudentsInDb == 0) {
			enErr = ErrorStatus.ERR_STUDENT_DATABASE_EMPTY;
			return false;
		}

		nStudents = 0;

			//stuTable.PrintTable();
		//System.out.println("Reading students table...");

		for (int n = 0 ; n < nStudentsInDb ; n++) {
			String strUId = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentUid.enVal()));
			//System.out.println("Reading student: " + strUId);
			String strId = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentId.enVal()));
			String strFirstName = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentFName.enVal()));
			String strMidName = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentMName.enVal()));
			String strLastName = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentLName.enVal()));
			int nSSN = Integer.parseInt(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentSSN.enVal()));
			String strAddrs1 = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentAddrs1.enVal()));
			String strAddrs2 = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentAddrs2.enVal()));
			String strCity = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentAddrsCity.enVal()));
			String strState = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentAddrsState.enVal()));
			String strZip = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentAddrsZip.enVal()));
			int nAge = Integer.parseInt(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentAge.enVal()));
			String strGndr = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentGender.enVal()));
			String strYear = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentYearOfStudy.enVal()));
			String strEnrolStatus = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentEnrollmentStatus.enVal()));
			String strEnrolMjr = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentEnrolledMajor.enVal()));
			int nRegdCourses = Integer.parseInt(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentNRegdCourses.enVal()));
			String strEmail = new String(stuTable.ReadAt(n, StudentTableIndices.enIndxStudentEmail.enVal()));

			//System.out.println("...Read Student"+strUId);
			char cGen = strGndr.charAt(0);

			Student stu = new Student(strUId, strId, strFirstName, strMidName, strLastName, strAddrs1, strAddrs2,
					strCity, strState, strZip, nAge, cGen, nSSN, strEmail, strYear, strEnrolStatus, strEnrolMjr, nRegdCourses);

			lstStudents.add(stu);
			nStudents++;
		}

		if (enReadStatus == ReadStatus.READ_TEACHER)
			enReadStatus = ReadStatus.READ_ALL;
		else
			enReadStatus = ReadStatus.READ_STUDENT;

		return true;
	}

	private boolean ReadTeachers() {

		//System.out.println("Reading Teachers Table...");
		tchrTable = new Table(strDatabaseFileTeachers, strDelim);
		if (tchrTable.IsErr()) {
			enErr = ErrorStatus.ERR_TEACHER_DATABASE;
			return false;
		}

		int nTeachersInDb = tchrTable.GetNumberOfLines();

		if (nTeachersInDb == 0) {
			enErr = ErrorStatus.ERR_TEACHER_DATABASE_EMPTY;
			return false;
		}

		nTeachers = 0;

		//tchrTable.PrintTable();
		//System.out.println("# of teachers.. "+nTeachersInDb);
		for (int n = 0 ; n < nTeachersInDb ; n++) {

			String strUId = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherUid.enVal()));
			//System.out.println("Reading Teacher: "+strUId);
			String strId =  new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherId.enVal()));
			String strFirstName = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherFName.enVal()));
			String strMidName = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherMName.enVal()));
			String strLastName = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherLName.enVal()));
			int nSSN = Integer.parseInt(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherSSN.enVal()));
			String strAddrs1 = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherAddrs1.enVal()));
			String strAddrs2 = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherAddrs2.enVal()));
			String strCity = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherAddrsCity.enVal()));
			String strState = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherAddrsState.enVal()));
			String strZip = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherAddrsZip.enVal()));
			int nAge = Integer.parseInt(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherAge.enVal()));
			String strGndr = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherGender.enVal()));
			String strEmail = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherEmail.enVal()));
			String strOffHrs = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherOfficeHrs.enVal()));
			String strRoomNo = new String(tchrTable.ReadAt(n, TeacherTableIndices.enIndxTeacherRoomNumber.enVal()));
			int nTeaching = Integer.parseInt(tchrTable.ReadAt(n, TeacherTableIndices.enIndxNumCoursesTeaching.enVal()));
			//System.out.println("...Read Teacher: "+strUId);

			char cGen = strGndr.charAt(0);

			Teacher tchr = new Teacher(strUId, strId, strFirstName, strMidName, strLastName, strAddrs1, strAddrs2,
					strCity, strState, strZip, nAge, cGen, nSSN, strEmail, strOffHrs, strRoomNo);

			if (nTeaching > 0) {
				for (int m = 0; m < nTeaching ; m++) {
					int crseIndx = TeacherTableIndices.enIndxNumCoursesTeaching.enVal()+m+1;
					String crseId = tchrTable.ReadAt(n, crseIndx);
					tchr.addCourse(crseId);
				}
			}
			lstTeachers.add(tchr);
			nTeachers++;
		}

		if (enReadStatus == ReadStatus.READ_STUDENT)
			enReadStatus = ReadStatus.READ_ALL;
		else
			enReadStatus = ReadStatus.READ_TEACHER;

		return true;
	}

	public UserDatabase() {
		lstStudents  = new ArrayList<Student>();
		lstTeachers  = new ArrayList<Teacher>();
		nStudents = 0;
		nTeachers = 0;
		enReadStatus = ReadStatus.READ_INIT;
		enErr = ErrorStatus.ERR_NOT_READY;

		this.ReadStudents();
		this.ReadTeachers();
	}

	public boolean IsReady() {
		return enReadStatus == ReadStatus.READ_ALL;
	}

	public boolean AddStudent(Student stuObj) {
		lstStudents.add(stuObj);
		nStudents++;

		String str = stuObj.toString();
		//System.out.println("Adding Student: ");
		//System.out.println(str);
		stuTable.AddALine(str);

		return stuTable.WriteToFile(strDatabaseFileStudentsW);
	}

	public boolean UpdateStudent(Student stuObj, StudentTableIndices [] enFields, int nFields) {
		Student stu = this.GetStudent(stuObj.iD());
		if (stu == null)
			return false;

		String str = new String();

		for (int n = 0; n < nFields ; n++) {
			StudentTableIndices enF = enFields[n];
			switch(enF) {
				case  enIndxStudentUid:
					  str = stuObj.userId();
					  stu.setUserId(str);
					break;
				case  enIndxStudentId:
					  str = stuObj.iD();
					  stu.setId(str);
					break;
				case  enIndxStudentFName:
					  str = stuObj.firstName();
					  stu.setFirstName(str);
					break;
				case  enIndxStudentMName:
					  str = stuObj.midName();
					  stu.setMidName(str);
					break;
				case  enIndxStudentLName:
					  str = stuObj.lastName();
					  stu.setLastName(str);
					break;
				case  enIndxStudentAddrs1:
					  str = stuObj.address1();
					  stu.setAddress1(str);
					break;
				case  enIndxStudentAddrs2:
					  str = stuObj.address2();
					  stu.setAddress2(str);
					break;
				case  enIndxStudentAddrsCity:
					  str = stuObj.addressCity();
					  stu.setCity(str);
					break;
				case  enIndxStudentAddrsState:
					  str = stuObj.addressState();
					  stu.setState(str);
					break;
				case  enIndxStudentAddrsZip:
					  str = stuObj.addressZip();
					  stu.setZip(str);
					break;
				case  enIndxStudentEmail:
					  str = stuObj.email();
					  stu.setEmail(str);
					break;
				case  enIndxStudentAge:
					  str = Integer.toString(stuObj.age());
					  stu.setAge(stuObj.age());
					break;
				case  enIndxStudentGender:
					  str = Character.toString(stuObj.gender());
					  stu.setGender(stuObj.gender());
					break;
				case  enIndxStudentSSN:
					  str = Integer.toString(stuObj.sSN());
					  stu.setSSN(stuObj.sSN());
					break;
				case  enIndxStudentYearOfStudy:
					  str = stuObj.getYearsOfStudy();
					  stu.setYearsOfStudy(str);
					break;
				case  enIndxStudentEnrollmentStatus:
					  str = stuObj.getEnrollmentStatus();
					  stu.setEnrollmentStatus(str);
					break;
				case  enIndxStudentEnrolledMajor:
					  str = stuObj.getEnrolledMajor();
					  stu.setEnrolledMajor(str);
					break;
				case  enIndxStudentNRegdCourses:
					  str = Integer.toString(stuObj.getNRegdCourses());
					  stu.setRegdCourses(stuObj.getNRegdCourses());
					break;
			}
			this.SetStudentTableAt(stuObj.iD(), enF.enVal(), str);
		}

		return true;
	}

	public boolean AddTeacher(Teacher tchrObj) {
		lstTeachers.add(tchrObj);
		nTeachers++;

		String str = tchrObj.toString();
		tchrTable.AddALine(str);

		return tchrTable.WriteToFile(strDatabaseFileTeachersW);
	}

	public int NumberOfStudents() {
		return nStudents;
	}

	public int NumberOfTeachers() {
		return nTeachers;
	}

	public int NumberOfUsers() {
		return (nStudents+nTeachers);
	}

	public Student GetStudentByUsrId(String strUserId) {
		int nSize = lstStudents.size();

		if (nSize == 0)
			return null;

		for (int n = 0; n < nSize ; n++) {
			Student stu = lstStudents.get(n);
			String strStuUid = stu.userId();
			if (StringUtils.equals(strStuUid, strUserId)) {
				return stu;
			}
		}

		return null;
	}

	public Student GetStudent(String strId) {
		int nSize = lstStudents.size();

		if (nSize == 0)
			return null;

		for (int n = 0; n < nSize ; n++) {
			Student stu = lstStudents.get(n);
			String strStuUid = stu.iD();
			if (StringUtils.equals(strStuUid, strId)) {
				return stu;
			}
		}

		return null;
	}


	public boolean SetStudentTableAt(String strStuId, int colId, String strValue) {
		int nSize = lstStudents.size();

		if (nSize == 0)
			return false;

		for (int n = 0; n < nSize ; n++) {
			Student stu = lstStudents.get(n);
			String strId = stu.iD();
			if (StringUtils.equals(strId, strStuId)) {
				stuTable.SetStringAt(n, colId, strValue);
				return true;
			}
		}

		return false;
	}

	public Teacher GetTeacher(String strUserId) {
		int nSize = lstTeachers.size();

		if (nSize == 0)
			return null;

		for (int n = 0; n < nSize ; n++) {
			Teacher tchr = lstTeachers.get(n);
			String strTchrUid = tchr.userId();
			if (StringUtils.equals(strTchrUid, strUserId))
				return tchr;
		}

			return null;
	}

	public String[] GetAllStudentsbyId() {
		String [] stuIds = new String [nStudents];

		if (nStudents > 0) {
			for (int n = 0; n < nStudents ; n++) {
				Student stu = lstStudents.get(n);
				stuIds[n] = stu.iD();
			}
		}

		return stuIds;
	}

	public boolean SetCoursesRegdForStudent(String strUsrId, int nCrses) {
			Student stu = this.GetStudent(strUsrId);

			//System.out.println("....Update called.. ");
			if (stu == null)
				return false;

			stu.setRegdCourses(nCrses);
			this.SetStudentTableAt(strUsrId, StudentTableIndices.enIndxStudentNRegdCourses.enVal(), Integer.toString(nCrses));
			return true;
	}

	public boolean UpdateDatabase() {
		return stuTable.WriteToFile(strDatabaseFileStudentsW);
	}

	public boolean CloseDatabase() {
		if (stuTable.WriteToFile(strDatabaseFileStudentsW) == false)
			return false;

		if (tchrTable.WriteToFile(strDatabaseFileTeachersW) == false)
			return false;

		return true;
	}
}

